﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace frag_boardgame
{
    public class AI
    {

        public static void PlayAI(Game game)
        {
            Player player = game.Data.CurrentPlayer;
            ShootingValues shootingValues;
            bool isGameOver = false, isOutOfMoves = false;
            
            while (//the game didn't end
                !game.getLogic().isGameOver(game.Data.GetPlayersList()) &&
                player.ShotsLeft > 0 &&
                //and the turn wasn't passed to another player(could happen by player walking all the allowed moves)
                game.Data.CurrentPlayer == player)
            {
                Player target = ChooseTarget(player, game);
                //if the distance is bigger then throwing 4 for each dice the player has for shot dice, try to get closer
                //TODO:if the number of dice player can throw to determine hit are changed from 2, need to change this accordingly
                if (player.calculateDistance(target, game.Data) > 4 * 2)
                {
                    AI.MoveToTarget(player, target, game);
                }
                if (!game.getLogic().isGameOver(game.Data.GetPlayersList()) &&
                    !game.getLogic().isTurnOver(player))
                {
                  shootingValues=  game.Shoot(target.ID);
                  if (shootingValues.Options == ShootingOptionsEnum.GameOver)
                      isGameOver = true;
                  if (shootingValues.Options == ShootingOptionsEnum.CantShoot)
                  {
                      isOutOfMoves = true;
                      break;
                  }
                }
            }
            //if it's still my turn,turn could have finished if the AI player moved all his allowed moves
            if (!isGameOver)
                if (isOutOfMoves || (game.Data.CurrentPlayer == player &&
                    player.ShotsLeft == 0))
                {
                    game.FinishCurrentTurn();
                }

        }

        public static Player ChooseTarget(Player player, Game game)
        {
            List<Player> players = (List<Player>)game.Data.GetPlayersList();
            List<Player> InSight = new List<Player>();
            for (int i = 0; i < players.Count; i++)
            {
                Player pl = players.ElementAt(i);
                if ((pl != player) && (game.getLogic().canShoot(player, pl, game.Data)))
                {
                    InSight.Add(pl);
                }
            }
            if (InSight.Count == 1) return InSight.ElementAt(0);
            if (InSight.Count > 1) return AI.findClosest(player, InSight, game);
            else return AI.findClosest(player, players, game);
        }
        /*
        public static Player findClosest(Player player, List<Player> players, Game game)
        {
            Player closest = null;
            float dist;
            int j = 0;
            while ((players.ElementAt(j) == player) && (j < players.Count))
            {
                j++;
            }
            if (j == players.Count) return null;
            dist = player.calculateDistance(players.ElementAt(j), game.Data);
            for (int i = 0; i < players.Count; i++)
            {
                Player pl = players.ElementAt(i);
                if (pl != player)
                {
                    float tempDist = player.calculateDistance(pl, game.Data);
                    if (tempDist < dist)
                    {
                        dist = tempDist;
                        closest = pl;
                    }
                }
            }
            return closest;
        }
         */

        public static Player findClosest(Player player, List<Player> players, Game game)
        {
            if (players.Count == 0)
                return null;
            else if (players.Count == 1)
                return players.ElementAt(0);
            else
            {
                Player closest = players.ElementAt(0);
                float dist = float.MaxValue;
                
                for (int i = 1; i < players.Count; i++)
                {
                    Player pl = players.ElementAt(i);
                    if (pl != player)
                    {
                        float tmpDist = pl.calculateDistance(player, game.Data);
                        if (tmpDist < dist)
                        {
                            dist = tmpDist;
                            closest = pl;
                        }
                    }
                }
                return closest;
            }

        }


        public static void MoveToTarget(Player player, Player target, Game game)
        {
            Direction dir = decideDirection(player,target,game);
            float numOfMoves = 0;
            int col=player.Column, row=player.Row;
            if (dir == Direction.Up || dir == Direction.Down)
            {
                numOfMoves = Math.Abs(player.calculateVerticalDistance(target,game.Data));
            }
            else
            {
                numOfMoves = Math.Abs(player.calculateHorizontalDistance(target,game.Data));
            }
            numOfMoves= Math.Min(numOfMoves -2, player.MovesLeft);
            for (int i = 0; i < numOfMoves; i++)
            {
                //we can stiil move in that direction
                if (AI.canMoveInDirection(player,dir,game) && 
                    //we still need to move in that direction
                    (dir == AI.decideHorizontalDirection(player,target,game) || dir == AI.decideVerticalDirection(player,target,game)))
                {
                    game.MovePlayer(1, dir);
                }
                else
                {
                    //try moving in the other needed direction
                    if (dir == Direction.Right || dir == Direction.Left)
                    {
                        dir = AI.decideVerticalDirection(player, target, game);
                    }
                    else
                    {
                        dir = AI.decideHorizontalDirection(player, target, game);
                    }
                }
            }
            //if the player is not out of moves
            if (player.MovesLeft > 0 &&
                //the distance is bigger then 6 or he can't shoot the player
                (player.calculateDistance(target, game.Data) > 6 || !game.getLogic().canShoot(player, target, game.Data)))
            {
                //check that the player can move towards the target
                if (AI.canMoveInDirection(player,decideHorizontalDirection(player, target, game),game) ||
                        AI.canMoveInDirection(player,decideVerticalDirection(player, target, game),game))
                {
                    //keep moving
                    MoveToTarget(player, target, game);
                }
            }
        }

        private static bool canMoveInDirection(Player player,Direction dir, Game game)
        {
            int col = player.Column;
            int row = player.Row;
            switch (dir)
                {
                    case Direction.Down:
                        {
                            row = row + 1;
                            break;
                        }
                    case Direction.Up:
                        {
                            row = row - 1;
                            break;
                        }
                    case Direction.Left:
                        {
                            col = col - 1;
                            break;
                        }
                    case Direction.Right:
                        {
                            col = col + 1;
                            break;
                        }
                }
            return game.getLogic().CanMoveToSquare(player, row, col, game.Data);
        }

        private static Direction decideDirection(Player player, Player target, Game game)
        {
            if (Math.Abs(player.calculateHorizontalDistance(target, game.Data)) > Math.Abs(player.calculateVerticalDistance(target, game.Data)))
            {
                return decideHorizontalDirection(player, target, game);
            }
            else
            {
                return decideVerticalDirection(player, target, game);
            }

        }

        private static Direction decideHorizontalDirection(Player player, Player target, Game game)
        {
           if (player.calculateHorizontalDistance(target,game.Data) > 0)
                return Direction.Left;
           if (player.calculateHorizontalDistance(target, game.Data) < 0)
                return Direction.Right;
           return AI.decideVerticalDirection(player, target, game);
        }

        private static Direction decideVerticalDirection(Player player, Player target, Game game)
        {
            if (player.calculateVerticalDistance(target, game.Data) > 0)
                return Direction.Up;
            if (player.calculateVerticalDistance(target, game.Data) < 0)
                return Direction.Down;
            return AI.decideHorizontalDirection(player, target, game);
        }

        public static void ShotTarget(Player target, Game game)
        {
            game.Shoot(target.ID);
        }
    }
 
}
